﻿using WebApplication1.Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;

namespace WebApplication1.Microsoft.Extensions;

/// <summary>
/// 启动类，用于配置服务和中间件
/// </summary>
public static partial class ServiceExtensions
{

    [LoggerMessage(Level = LogLevel.Debug, Message = "impl: {implType}, for: {serviceType}, with Key: {serviceKey}. lifetime: {lifetime}")]
    static partial void LogKeyServices(this ILogger logger, Type? implType, Type serviceType, string? serviceKey, ServiceLifetime lifetime);

    [LoggerMessage(Level = LogLevel.Debug, Message = "impl: {implType}, for: {serviceType},  lifetime: {lifetime}")]
    static partial void LogServices(this ILogger logger, Type? implType, Type serviceType, ServiceLifetime lifetime);



    /// <summary>
    /// 配置服务
    /// </summary>
    /// <param name="sc">服务集合</param>
    public static void ScanServices(this IServiceCollection sc, ILogger? logger = null)
    {

        //  加载当前目录下，所有的程序集。
        //  避免有些组件在当前上下文，未被使用而未加载。
        LoadCurrentFloder();

        var sysServiceCount = sc.Count;
        //logger.LogDebug("system services {count}", sc.Count);
        sc.AutoRegisterServices();
        logger?.LogDebug("add {count} services", sc.Count - sysServiceCount);

        for (int i = sysServiceCount; i < sc.Count; i++)
        {
            var tmp = sc[i];
            if (tmp.IsKeyedService)
            {
                logger?.LogKeyServices(tmp.KeyedImplementationType, tmp.ServiceType, tmp.ServiceKey?.ToString(), tmp.Lifetime);
            }
            else
            {
                logger?.LogServices(tmp.ImplementationType, tmp.ServiceType, tmp.Lifetime);
            }
        }


    }

    /// <summary>
    /// 自动注册服务
    /// </summary>
    /// <param name="sc">服务集合</param>
    private static void AutoRegisterServices(this IServiceCollection sc)
    {
        AutoRegisterServices(sc,
            ass => ass.FullName != null && ass.FullName.StartsWith("WebApplication1", StringComparison.OrdinalIgnoreCase),
            type => type.FullName != null && type.FullName.Contains("Provider", StringComparison.OrdinalIgnoreCase));
    }

    static Func<Type, bool> _interfaceFilter = (t) => t.FullName != null && !t.FullName.StartsWith("WebApplication1.Microsoft.Extensions.DependencyInjection");

    /// <summary>
    /// 加载目录下所有未发现的程序集
    /// </summary>
    /// <param name="path"></param>
    private static void LoadFolder(string path)
    {
        var assemblies = AssemblyLoadContext.Default.Assemblies;
        var assPath = assemblies.Select(p => p.Location).ToArray();
        var findAssemblies = Directory.GetFiles(path, "*.*", SearchOption.AllDirectories)
            .Where(f => f.EndsWith(".dll") && !assPath.Contains(f));
        foreach (var f in findAssemblies)
        {

            AssemblyLoadContext.Default.LoadFromAssemblyPath(f);
        }
    }

    /// <summary>
    /// 加载当前目录下所有未发现的程序集
    /// </summary>
    private static void LoadCurrentFloder()
    {
        var file = AppContext.BaseDirectory;

        string? path = null;
        if (!string.IsNullOrEmpty(file))
            path = Directory.GetParent(file)!.FullName;

        path ??= Environment.CurrentDirectory;

        //Console.WriteLine("Load Folder All assemblies: {0}", path);
        LoadFolder(path);

    }



    /// <summary>
    /// 自动注册服务
    /// </summary>
    /// <param name="sc">服务集合</param>
    /// <param name="assemblyFilter">程序集过滤器</param>
    /// <param name="typeFilter">类型过滤器</param>
    private static void AutoRegisterServices(this IServiceCollection sc, Func<Assembly, bool> assemblyFilter, Func<Type, bool> typeFilter)
    {
        var assemblies = AssemblyLoadContext.Default.Assemblies;
        assemblies = assemblies.Where(assemblyFilter);

        if (assemblies.Any())
        {
            foreach (var assembly in assemblies)
            {
                var types = assembly.GetTypes().Where(typeFilter);
                types = types.Where(t => !(t.IsAbstract || t.IsInterface));

                foreach (var type in types)
                {
                    var lifeTime = GetLifetime(type);
                    if (lifeTime == null)
                        continue;
                    var keyedName = type.GetKeyedName();

                    var exposeTypes = type.GetServiceTypes(out var disableKeyedServices);
                    var interfaces = type.GetInterfaces().Where(_interfaceFilter).ToArray();


                    if (exposeTypes != null)
                        interfaces = exposeTypes;

                    type.RegisterInterface(sc, interfaces);

                    if (!disableKeyedServices && !string.IsNullOrEmpty(keyedName))
                        type.RegisterInterfaceWithKeyed(sc, keyedName, interfaces);

                    if (exposeTypes == null)
                        type.RegisterSelf(sc);
                }
            }
        }
    }

    /// <summary>
    /// 获取生命周期
    /// </summary>
    /// <param name="type">类型</param>
    /// <returns>服务生命周期</returns>
    private static ServiceLifetime? GetLifetime(this Type type)
    {
        if (type.IsAssignableTo(typeof(IDependency)))
        {
            switch (type)
            {
                case Type t when t.IsAssignableTo(typeof(ISingletonDependency)):
                    return ServiceLifetime.Singleton;
                case Type t when t.IsAssignableTo(typeof(IScopeDependency)):
                    return ServiceLifetime.Scoped;
                case Type t when t.IsAssignableTo(typeof(ITransientDependency)):
                    return ServiceLifetime.Transient;
            }
        }
        // todo :添加对特性标签的识别
        return null;
    }

    private static Type[]? GetServiceTypes(this Type type, out bool disableKeyedServices)
    {
        var attr = type.GetCustomAttribute<ExposeAttribute>();
        disableKeyedServices = attr?.DisableKeyedService ?? false;
        var serviceTypes = attr?.ServiceType;
        return serviceTypes?.Length > 0 ? serviceTypes : null;
    }

    /// <summary>
    /// 获取键名
    /// </summary>
    /// <param name="type">类型</param>
    /// <returns>键名</returns>
    private static string? GetKeyedName(this Type type)
    {
        var name = type.GetCustomAttribute<KeyedServiceAttribute>()?.Name;
        if (name == null)
        {
            var existsKeyedName = type.GetInterface(nameof(IKeyedService));
            if (existsKeyedName != null)
            {
                var interfaces = type.GetInterfaces().Where(_interfaceFilter);
                interfaces = interfaces.Where(t => !t.IsGenericType);   // 去除泛型接口
                if (!interfaces.Any() || interfaces.Count() > 1)        //  如果没有实现的接口，或者实现了多个，则不支持Keyed
                {
                    return null;
                }
                // 去除接口的首字母， 一般是I
                var fex = interfaces.First().Name[1..];
                //  去除和接口名称相同的部分，做为KeyedName， 比如ICommand接口下的BaiduCommand, 这时候KeyedName为Baidu
                name = type.Name.Replace(fex, "");
            }
        }
        return name;
    }

    /// <summary>
    /// 获取注册函数
    /// </summary>
    /// <param name="sc">服务集合</param>
    /// <param name="lifetime">生命周期</param>
    /// <returns>注册函数</returns>
    private static Func<Type, Type, IServiceCollection>? GetRegisterFunc(this IServiceCollection sc, ServiceLifetime lifetime)
    {
        Func<Type, Type, IServiceCollection>? func = null;

        switch (lifetime)
        {
            case ServiceLifetime.Singleton:
                func = sc.AddSingleton;
                break;
            case ServiceLifetime.Transient:
                func = sc.AddTransient;
                break;
            case ServiceLifetime.Scoped:
                func = sc.AddScoped;
                break;
        }
        return func;
    }

    /// <summary>
    /// 获取键名注册函数
    /// </summary>
    /// <param name="sc">服务集合</param>
    /// <param name="lifetime">生命周期</param>
    /// <returns>键名注册函数</returns>
    private static Func<Type, object, Type, IServiceCollection>? GetKeyedRegisterFunc(this IServiceCollection sc, ServiceLifetime lifetime)
    {
        Func<Type, object, Type, IServiceCollection>? func = null;

        switch (lifetime)
        {
            case ServiceLifetime.Singleton:
                func = sc.AddKeyedSingleton;
                break;
            case ServiceLifetime.Transient:
                func = sc.AddKeyedTransient;
                break;
            case ServiceLifetime.Scoped:
                func = sc.AddKeyedScoped;
                break;
        }
        return func;
    }

    /// <summary>
    /// 注册接口
    /// </summary>
    /// <param name="type">类型</param>
    /// <param name="sc">服务集合</param>
    /// <param name="interfaces">接口集合</param>
    private static void RegisterInterface(this Type type, IServiceCollection sc, params Type[] interfaces)
    {
        foreach (var iface in interfaces)
        {
            var lifeTime = iface.GetLifetime();
            if (lifeTime.HasValue)
            {
                var func = sc.GetRegisterFunc(lifeTime.Value);
                func?.Invoke(iface, type);
            }
        }
    }

    /// <summary>
    /// 注册自身
    /// </summary>
    /// <param name="type">类型</param>
    /// <param name="sc">服务集合</param>
    private static void RegisterSelf(this Type type, IServiceCollection sc)
    {
        var lifeTime = type.GetLifetime();
        if (lifeTime.HasValue)
        {
            var func = sc.GetRegisterFunc(lifeTime.Value);
            func?.Invoke(type, type);
        }
    }

    /// <summary>
    /// 注册键名接口
    /// </summary>
    /// <param name="type">类型</param>
    /// <param name="sc">服务集合</param>
    /// <param name="keyedName">键名</param>
    /// <param name="interfaces">接口集合</param>
    private static void RegisterInterfaceWithKeyed(this Type type, IServiceCollection sc, string keyedName, params Type[] interfaces)
    {
        var lifeTime = type.GetLifetime();
        if (lifeTime == null)
        {
            return;
        }
        foreach (var iface in interfaces)
        {
            var func = sc.GetKeyedRegisterFunc(lifeTime.Value);
            func?.Invoke(iface, keyedName, type);
        }
    }
}
